//
//  BNTabPageView.swift
//  BNUQuoteTrade
//
//  Created by bebull on 2021/12/22.
//

import Foundation
import UIKit

public typealias BNTabPageScrollBlock = ((UIScrollView) -> Void)


protocol BNTabPageScrollViewProtocol {
    func pageScrollView() -> UIScrollView
    func pageScrollViewDidScroll(callback: BNTabPageScrollBlock)
}

@objc protocol BNTabPageViewDelegate: NSObjectProtocol {
    @objc optional func headerViewInTabPage() -> UIView
    @objc optional func heightForHeaderViewInTabPage() -> CGFloat
    
    func tabViewInTabPage() -> BNTabView
    func heightForTabViewInTabPage() -> CGFloat
    func pageViewInTabPage() -> BNPageView
    func heightForPageViewInTabPage() -> CGFloat
}


class BNTabPageView: UIView {

    // MARK: - Properties
    // MARK: Public
    weak var delegate: BNTabPageViewDelegate?

    lazy var mainTableView: BNTabMainTableView = {
        let view = BNTabMainTableView(frame: .zero, style: .plain)
        view.showsVerticalScrollIndicator = false
        view.showsHorizontalScrollIndicator = false
        view.separatorStyle = .none
        view.dataSource = self
        view.delegate = self
        view.decelerationRate = .normal
        
        if let hasSel = delegate?.responds(to: #selector(headerViewInTabPage)), hasSel {
            view.tableHeaderView = delegate?.headerViewInTabPage?()
        }
        view.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        if #available(iOS 11.0, *) {
            view.contentInsetAdjustmentBehavior = .never
        } else {
            view.contentInset = .zero
        }
        return view
    }()

    var currentScrollingView: UIScrollView?

    // MARK: Private
    private var tabView: BNTabView?

    private var pageView: BNPageView?

    // MARK: - Init

    init(delegate: BNTabPageViewDelegate) {
        super.init(frame: .zero)
        self.delegate = delegate
        initializeViews()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - Override
    override func layoutSubviews() {
        super.layoutSubviews()
        mainTableView.snp.remakeConstraints { make in
            make.left.top.right.bottom.equalToSuperview()
        }
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if let hasSel = delegate?.responds(to: #selector(heightForHeaderViewInTabPage)), hasSel {
            guard let height = delegate?.heightForHeaderViewInTabPage?() else {
                return
            }
            let headerHeight = ceilf(Float(height))
            
            if let view = currentScrollingView, view.contentOffset.y > 0 {
                mainTableView.contentOffset = CGPoint(x: 0.0, y: CGFloat(headerHeight))
            }
            if scrollView.contentOffset.y < CGFloat(headerHeight) {
                let viewControllers = pageView?.viewControllers
                for viewController in viewControllers ?? [] {
                    if let controller = viewController as? BNTabPageScrollViewProtocol {
                        controller.pageScrollView().contentOffset = .zero
                    }
                }
            }
        }
        
    }
    // MARK: - Publics
    func reloadData() {
        if let hasSel = delegate?.responds(to: #selector(headerViewInTabPage)), hasSel {
            mainTableView.tableHeaderView?.layoutIfNeeded()
            mainTableView.tableHeaderView = delegate?.headerViewInTabPage?()
        }
        mainTableView.reloadData()
        tabView?.reloadData()
        pageView?.reloadData()
        configListViewDidScrollCallback()
    }

    func scrollToTop() {
        let delegate = self.delegate
        self.delegate = nil
        currentScrollingView?.setContentOffset(.zero, animated: false)
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
            guard let self = self else { return }
            DispatchQueue.main.async {
                self.delegate = delegate
                self.mainTableView.setContentOffset(.zero, animated: false)
                self.reloadData()
            }
        }
    }
}

extension BNTabPageView {

    // MARK: - Notifications

    // MARK: - Action
    
    @objc func headerViewInTabPage() {}
    
    @objc func heightForHeaderViewInTabPage() {}

    // MARK: - Privates
    private func initializeViews() {
        tabView = delegate?.tabViewInTabPage()
        tabView?.needLayout = true
        pageView = delegate?.pageViewInTabPage()
        pageView?.needLayout = true
//        tabView?.pageView = pageView
        
        tabView?.delegate = self
        pageView?.delegate = self
        
        addSubview(mainTableView)
        configListViewDidScrollCallback()
    }
    
    private func configListViewDidScrollCallback() {
        let viewControllers = pageView?.viewControllers
        for viewController in viewControllers ?? [] {
            if let controller = viewController as? BNTabPageScrollViewProtocol {
                controller.pageScrollViewDidScroll(callback: { [weak self] (scrollView) in
                    guard let self = self else { return }
                    self.pageScrollViewDidScroll(scrollView: scrollView)
                })
            }
        }
    }
    
    private func pageScrollViewDidScroll(scrollView: UIScrollView) {
        currentScrollingView = scrollView
        if let hasSel = delegate?.responds(to: #selector(heightForHeaderViewInTabPage)), hasSel {
            guard let height = delegate?.heightForTabViewInTabPage() else {
                return
            }
            let headerHeight = ceilf(Float(height))
            
            if mainTableView.contentOffset.y < CGFloat(headerHeight) {
                scrollView.contentOffset = .zero
                scrollView.showsVerticalScrollIndicator = false
            } else {
                mainTableView.contentOffset = CGPoint(x: 0.0, y: CGFloat(headerHeight))
                scrollView.showsVerticalScrollIndicator = true
            }
        }
    }
}

extension BNTabPageView: BNTabViewDelegate {
    func tabView(_ tabView: BNTabView, didSelectedItemAt index: Int, with scrolling: Bool) {
        ///
    }
    
    func contentScrollView(offsetAt index: Int) {
        if let view = pageView {
            let offset = CGPoint(x: CGFloat(index) * view.contentView.bounds.size.width, y: 0.0)
            pageView?.contentView.setContentOffset(offset, animated: false)
        }
    }
    
}

extension BNTabPageView: BNPageViewDelegate {
    
    /// 通知代理，contentView已经发生偏移
    func contentView(_ contentView: UIScrollView, didChanged offset: CGPoint) {
        if let view = pageView {
            self.tabView?.contentScrollViewDidChanged(offset, width: view.contentView.bounds.size.width)
        }
    }
}

extension BNTabPageView: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return delegate?.heightForPageViewInTabPage() ?? 0.0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.backgroundColor = .clear
        for view in cell.contentView.subviews {
            view.removeFromSuperview()
        }
        cell.selectionStyle = .none
        guard let view = pageView  else {
            return cell
        }
        view.needLayout = true
        view.frame = cell.contentView.bounds
        cell.contentView.addSubview(view)
        return cell
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }
    
}

extension BNTabPageView: UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return delegate?.heightForTabViewInTabPage() ?? 0.0
    }
    
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return tabView
    }
    
    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        return CGFloat.leastNormalMagnitude
    }
    
    func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        let footer = UIView(frame: .zero)
        footer.backgroundColor = .clear
        return footer
    }
    
}

